import fs from 'fs'
import fg from 'fast-glob'

import { Options } from './options'
import * as template from './template'
import * as util from './util'

const pathConvertUrl = (pathStr: string, isLayout: boolean = false) => {
    // 取出路径中最后面的index，login/index => login
    let _path = pathStr.replace(/\/index$/, '')

    // 取出路径中最后 / 的内容， test/blog.w.o.layout => test/blog/w/o/layout
    const arr = _path.split('/')
    if (arr.length > 1) {
        const lastPath = arr.pop()!.replace(/\./g, '/')
        _path = arr.join('/') + '/' + lastPath
    }

    // 替换路由中的 __xx 为 :xx ，detail/__id => detail/:id
    _path = _path.replace(/\__(.*?)/g, ':$1')


    if ("index" === pathStr) {
        return "/"
    }

    if (isLayout) {
        _path = _path.replace(/[\/]?_layout$/, "")
    }

    return "/" + _path;

}


type RouteType = "element" | "component"

interface Layout {
    layoutDir: string,
    pages: string[]
}


const getLayoutInfo = (allViews: string[]) => {
    const layouts = allViews.filter(view => view.includes('_layout'));
    const layoutMap: { [key: string]: Layout } = {};
    const regex = /(.*)\/_layout/;
    layouts.forEach(layout => {
        const matches = regex.exec(layout);
        if (matches && matches.length > 1) {
            const key = matches[1];
            layoutMap[key] = {
                layoutDir: layout,
                pages: [],
            }
        }
    })

    const layoutKeys = Object.keys(layoutMap);
    layoutKeys.forEach(key => {
        allViews.forEach(item => {
            if (item.includes(key) && item !== layoutMap[key].layoutDir) {
                layoutMap[key].pages.push(item)
            }
        })
    })

    const checkLayout = util.checkNormalLayout(layoutKeys);

    const normalLayouts = allViews.filter(view => checkLayout(view));

    return {
        layoutMap,
        normalLayouts,
    }
}

const getPaths = (views: string[], options: Options) => {
    const regex = new RegExp(`${options.pageDir}/([^/]+(?:\/[^/]+)*)\.(${options.pageFileType.join("|")})$`)
    // console.log(regex)
    const paths = views
        .filter((item) => !item.includes('/components/'))
        .map((item) => {
            const match = item.match(regex)
            if (match) {
                return match[1]
            }
            return null
        })
        .filter((item) => item != null)
    return paths;
}

const handleNormalLayouts = (views: string[], options: Options, routeType: RouteType, layoutNum = "") => {
    const paths = getPaths(views, options);

    // console.log(paths)
    const routes = [] as string[];
    const lazyImportList = [] as string[];
    const importList = [] as string[];

    const srcAlias = options.pageDir.replace("./src", "@");

    paths.map((item, index) => {
        let filePath = `${options.pageDir}/${item}`;
        for (let fileType of options.pageFileType) {
            if (fs.existsSync(`${filePath}.${fileType}`)) {
                filePath += `.${fileType}`
                break;
            }
        }

        const content = fs.readFileSync(`${filePath}`, 'utf-8')
        const _metaAuth = util.getMetaParam(content, 'META_AUTH');
        const _metaLazy = util.getMetaParam(content, 'META_LAZY');
        let metaAuth = util.getMetaBooleanParam(_metaAuth, options.auth);
        let metaLazy = util.getMetaBooleanParam(_metaLazy, options.lazy);

        let compName = `App_${index}`
        if (layoutNum) {
            compName = `App_${layoutNum}_${index}`
        }
        const _path = pathConvertUrl(item);

        // console.log(item,_path)

        if (routeType === "element") {
            if (metaLazy) {
                lazyImportList.push(`const ${compName}=lazy(()=>import('${srcAlias}/${item}'));`);
            } else {
                importList.push(`import ${compName} from '${srcAlias}/${item}';`);
            }

            let _element = null;
            if (metaAuth) {
                _element = `<AuthRoute><Suspense fallback={<Loading />}><${compName} /></Suspense></AuthRoute>`
            } else {
                _element = `<Suspense fallback={<Loading />}><${compName} /></Suspense>`
            }

            routes.push(`  {path: "${_path}", element: ${_element}},`)
        } else {
            if (metaLazy) {
                routes.push(`  {path: "${_path}", lazy: () => import('${srcAlias}/${item}').then(module => ({ Component: module.default }))},`)
            } else {
                importList.push(`import ${compName} from '${srcAlias}/${item}';`);
                routes.push(`  {path: "${_path}", Component: ${compName}},`)
            }
        }

    })
    return Promise.resolve({
        lazyImportList,
        importList,
        routes,
        count: paths.length
    })
}


const handleLayouts = async (layoutMap: { [key: string]: Layout }, options: Options, routeType: RouteType) => {

    const srcAlias = options.pageDir.replace("./src", "@");
    const counts: number[] = [];
    const lazyImportList: string[] = [];
    const importList: string[] = [];
    const routes: string[] = [];

    Object.keys(layoutMap).map(async (item, index) => {
        counts.push(1);
        const layout = layoutMap[item];

        const regex = new RegExp(`${options.pageDir}/([^/]+(?:\/[^/]+)*)\.(${options.pageFileType.join("|")})$`)

        if (regex.test(layout.layoutDir)) {
            let filePath = `${layout.layoutDir}`;
            for (let fileType of options.pageFileType) {
                if (fs.existsSync(`${filePath}.${fileType}`)) {
                    filePath += `.${fileType}`
                    break;
                }
            }

            const content = fs.readFileSync(`${filePath}`, 'utf-8')
            const _metaAuth = util.getMetaParam(content, 'META_AUTH');
            const _metaLazy = util.getMetaParam(content, 'META_LAZY');
            let metaAuth = util.getMetaBooleanParam(_metaAuth, options.auth);
            let metaLazy = util.getMetaBooleanParam(_metaLazy, options.lazy);

            const compName = `Layout_${index}`
            const paths = getPaths([layout.layoutDir], options)
            const _path = pathConvertUrl(paths[0], true);

            const layoutAlias = layout.layoutDir.replace("./src", "@");

            if (routeType === "element") {
                if (metaLazy) {
                    lazyImportList.push(`const ${compName}=lazy(()=>import('${layoutAlias}'));`);
                } else {
                    importList.push(`import ${compName} from '${layoutAlias}';`);
                }
            }

            // 组装layout的pages
            const childrenObj = await handleNormalLayouts(layout.pages, options, routeType, index.toString());
            // console.log(childrenObj)
            counts.push(childrenObj.count);
            if (childrenObj.importList) {
                importList.push(...childrenObj.importList)
            }
            if (childrenObj.lazyImportList) {
                lazyImportList.push(...childrenObj.lazyImportList)
            }

            if (routeType === "element") {
                let _element = null;
                if (metaAuth) {
                    _element = `<AuthRoute><Suspense fallback={<Loading />}><${compName} /></Suspense></AuthRoute>`
                } else {
                    _element = `<Suspense fallback={<Loading />}><${compName} /></Suspense>`
                }

                routes.push(`  {path: "${_path}", element: ${_element}, children: [${childrenObj.routes.join("")}]}, `)
            } else {
                if (metaLazy) {
                    routes.push(`  {path: "${_path}", children: [${childrenObj.routes.join("")}], lazy: () => import('${layoutAlias}').then(module => ({ Component: module.default }))},`)
                } else {
                    importList.push(`import ${compName} from '${srcAlias}/${item}';`);
                    routes.push(`  {path: "${_path}", children: [${childrenObj.routes.join("")}], Component: ${compName}},`)
                }
            }
        }
    })

    return {
        lazyImportList,
        importList,
        routes,
        count: counts.reduce((accumulator, currentValue) => accumulator + currentValue, 0)
    }
}


const generateRouteTypes = async (options: Options, routeType: RouteType) => {

    // 获取全部页面
    const _routes = `${options.pageDir}/**/[\\w[-]*.(${options.pageFileType.join("|")})`
    const allViews = await fg(_routes, { onlyFiles: true })
    // console.log(allViews)

    // 分离 layout
    const { layoutMap, normalLayouts } = getLayoutInfo(allViews)

    const normalRoutes = await handleNormalLayouts(normalLayouts, options, routeType);

    const layoutRoutes = await handleLayouts(layoutMap, options, routeType);

    const lazyImportList = [...normalRoutes.lazyImportList, ...layoutRoutes.lazyImportList];
    const importList = [...normalRoutes.importList, ...layoutRoutes.importList];
    const routes = [...normalRoutes.routes, ...layoutRoutes.routes];
    const counts = [normalRoutes.count, layoutRoutes.count];

    return Promise.resolve({
        lazyImportList,
        importList,
        routes,
        count: counts.reduce((accumulator, currentValue) => accumulator + currentValue, 0)
    })
}

export const generate = async (options: Options) => {
    const { outputType } = options;
    const start = Date.now()
    const { lazyImportList, importList, routes, count } = await generateRouteTypes(options, "element")

    console.log(`scanned ${count} routes in ${Date.now() - start} ms`)

    const routesContent = util.format(template[`routes_${outputType}_template`], {
        name: options.name,
        routes: routes.join("\n"),
        lazyImport: lazyImportList.join("\n"),
        noLazyImport: importList.join("\n"),
    })

    if (!fs.existsSync(options.output)) {
        fs.promises.mkdir(options.output, { recursive: true })
    }
    const routesFilePath = `${options.output}/${options.name}.${outputType}`;
    await fs.promises.writeFile(routesFilePath, routesContent)

    const authRouteFilePath = `${options.output}/AuthRoute.${outputType}`
    if (!fs.existsSync(authRouteFilePath)) {
        const authRouteContent = util.format(template[`auth_route_${outputType}_template`], {})
        await fs.promises.writeFile(authRouteFilePath, authRouteContent)
    }

    const loadingFilePath = `${options.output}/Loading.${outputType}`
    if (!fs.existsSync(loadingFilePath)) {
        const loadingContent = util.format(template[`loading_template`], {})
        await fs.promises.writeFile(loadingFilePath, loadingContent)
    }

}


export const getAutoRouterJs = async (options: Options) => {
    const start = Date.now()
    const { importList, routes, count } = await generateRouteTypes(options, "component")
    console.log(`scanned ${count} routes in ${Date.now() - start} ms`)

    const routesContent = util.format(template.routes_js_template, {
        name: options.name,
        routes: routes.join("\n"),
        noLazyImport: importList.join("\n"),
    })

    return routesContent;
}
